import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.SwingUtilities;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

// 继承 JFrame 直接复用 swing 中窗口管理能力，为了子类可以直接调用 JFrame 的 public 方法
// 是 java 中事件处理的核心机制，组件和用户操作之间的核心机制
public class Calculator extends JFrame implements ActionListener {
    // 上下两个区域
    private JPanel north, center;
    // 显示计算过程及结果或日期时间
    private JTextField field;
    // 按钮
    private JButton[] btns;
    // 新增：声音开关状态
    private boolean isSoundOn = true;
    // 新增：历史计算记录列表
    private ArrayList<String> history = new ArrayList<>();
    // 新增：动态时间相关成员变量
    private Timer timer;
    private boolean isDynamicTime = false;


    // 初始化窗口
    public Calculator() {
        setTitle("计算器");
        setSize(500, 700);
        // 窗口关闭
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        // 窗口居中
        setLocationRelativeTo(null);

        initComponent();
        // 设置窗口显示
        setVisible(true);
        // 初始化计时器
        timer = new Timer(1000, e -> {
            if (isDynamicTime) {
                updateDynamicTime();
            }
        });
        timer.start(); // 启动计时器但不立即更新时间
    }


    // 新增：更新动态时间的方法
    private void updateDynamicTime() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        field.setText(sdf.format(new Date()));
    }


    private void initComponent() {
        // 创建面板容器
        // 容器面板 文本框区域
        north = new JPanel();
        // 容器面板 按钮区域
        center = new JPanel();
        // 文本框 显示计算过程、结果或者日期时间
        field = new JTextField();
        // 窗口大小
        field.setPreferredSize(new Dimension(500, 150));
        // 禁用通过电脑键盘文字输入编辑
        field.setEditable(false);
        // 设置输入框中文字大小
        field.setFont(new Font("SansSerif", Font.BOLD, 35));
        // 按钮文本内容
        String[] str = new String[]{
                "7", "8", "9", "+", "x²",
                "4", "5", "6", "-", "√",
                "1", "2", "3", "×", "%",
                "0", ".", "=", "÷", "1/x",
                "C", "B", "T", "S", "H"
        };
        //创建按钮数组
        btns = new JButton[str.length];
        //循环初始化每个按钮
        for (int i = 0; i < btns.length; i++) {
            btns[i] = new JButton(str[i]);
            //设置按钮字体
            btns[i].setFont(new Font("SansSerif", Font.BOLD, 25));

            //新增
            // 根据按钮类型设置不同背景色
            String btnText = str[i];// 获取按钮文本
            if (btnText.matches("[0-9]")) {
                btns[i].setBackground(new Color(173, 216, 230)); // 数字按钮 - 浅蓝色
            } else if (btnText.equals("=")) {
                btns[i].setBackground(new Color(50, 100, 50));   // 等号按钮 - 绿色
            } else if (btnText.equals(".")) {
                btns[i].setBackground(new Color(176, 224, 230)); // 小数点按钮 - 淡蓝色
            } else if (btnText.equals("x²") || btnText.equals("√") || btnText.equals("%") || btnText.equals("1/x") ||
                    "+-×÷".contains(btnText)) {
                btns[i].setBackground(new Color(255, 218, 185));   // 运算符按钮 - 橙色
            } else {
                btns[i].setBackground(new Color(211, 211, 211)); // 其他功能按钮 - 灰色
            }
            btns[i].setOpaque(true); // 使背景色可见
        }
        //新增接受吗

        // 设置布局管理器
        // 顶部面板用边界布局
        north.setLayout(new BorderLayout());
        // 中间面板使用 BorderLayout 居中
        center.setLayout(new GridLayout(5, 5));// 更改 5,4 改成 5,5

        // 组件设置
        // field 文本框 添加到 north 顶部面板 使用 BorderLayout 居中
        north.add(field, BorderLayout.CENTER);
        for (JButton btn : btns) {
            center.add(btn);
        }
        // 将顶部面板添加到窗口中
        add(north, BorderLayout.NORTH);
        add(center);
        for (JButton btn : btns) {
            btn.addActionListener(this);
        }
    }
    
    private void setBtnText(String text) {
        // 按下数字并把文字放到输入框中
        String oldText = field.getText();
        // 操作符和小数点的判断
        boolean isOperator = "+-×÷x²√%1/x".contains(text);
        boolean isDot = ".".equals(text);
       

        String speakText = ""; // 定义语音文本变量

        switch (text) {
            case "C": // 清空功能
                field.setText("");
                // 新增：语音提示
                speakText = "清空";
                break;
            case "B": // 退格功能
                // 获取输入框内容
                // 两种情况：输入框有文字；输入框为空
                String numText = field.getText();
                if (numText.length() != 0) {
                    // 字符串长度减一
                    String lastText = numText.substring(0, numText.length() - 1);
                    // 将截取的文本放入输入框
                    field.setText(lastText);
                    // 新增：语音提示
                    speakText = "退格";
                } else {
                    speakText = ""; // 空文本不语音
                    break;
                }
                break;
            case "T": // 获取当前时间
                isDynamicTime = !isDynamicTime;
                if (isDynamicTime) {
                    updateDynamicTime();
                    speak("开始显示动态时间");
                } else {
                    field.setText(""); // 关闭动态时间后清空文本框
                    speak("动态时间已关闭");
                }
                break;
            case "S":
                // 新增：语音提示
                //将变量的当前值取反后再赋回给该变量
                isSoundOn = !isSoundOn;
                speakText = isSoundOn ? "声音已开启" : "声音已关闭";
                break;
            case "H": // 历史计算记录
                StringBuilder historyText = new StringBuilder();
                for (String record : history) {
                    historyText.append(record).append("；");
                }
                field.setText(historyText.toString());
                speakText = "历史计算记录";
                break;
            case "x²": // 平方
                if (!oldText.isEmpty()) {
                    try {
                        double num = Double.parseDouble(oldText);
                        double result = num * num;
                        field.setText(String.valueOf(result));
                        history.add(oldText + "x² = " + result);
                        speakText = oldText + "的平方等于" + result;
                    } catch (NumberFormatException e) {
                        field.setText("输入错误");
                        speakText = "输入错误";
                    }
                }
                break;
            case "√": // 根号
                if (!oldText.isEmpty()) {
                    try {
                        double num = Double.parseDouble(oldText);
                        if (num >= 0) {
                            double result = Math.sqrt(num);
                            field.setText(String.valueOf(result));
                            history.add("√" + oldText + " = " + result);
                            speakText = oldText + "的平方根等于" + result;
                        } else {
                            field.setText("负数不能开平方");
                            speakText = "负数不能开平方";
                        }
                    } catch (NumberFormatException e) {
                        field.setText("输入错误");
                        speakText = "输入错误";
                    }
                }
                break;
            case "%": // 百分号
                if (!oldText.isEmpty()) {
                    try {
                        double num = Double.parseDouble(oldText);
                        double result = num / 100;
                        field.setText(String.valueOf(result));
                        history.add(oldText + "% = " + result);
                        speakText = oldText + "的百分之一等于" + result;
                    } catch (NumberFormatException e) {
                        field.setText("输入错误");
                        speakText = "输入错误";
                    }
                }
                break;
            case "1/x": // 取分数
                if (!oldText.isEmpty()) {
                    try {
                        double num = Double.parseDouble(oldText);
                        if (num != 0) {
                            double result = 1 / num;
                            field.setText(String.valueOf(result));
                            history.add("1/" + oldText + " = " + result);
                            speakText = "1除以" + oldText + "等于" + result;
                        } else {
                            field.setText("除数不能为零");
                            speakText = "除数不能为零";
                        }
                    } catch (NumberFormatException e) {
                        field.setText("输入错误");
                        speakText = "输入错误";
                    }
                }
                break;
            case "=":
                // 用户输入 1+1 执行计算操作，将结果展示到文本框
                calculate();
                // 新增：语音提示
                speakText = "等于 " + field.getText();
                break;
            case "×":
                // × 换成 *
                field.setText(field.getText() + "*");
                // 新增：语音提示
                speakText = "乘";
                break;
            case "÷":
                // ÷ 换成 /
                field.setText(field.getText() + "/");
                // 新增：语音提示
                speakText = "除";
                break;
            case "+":
                field.setText(field.getText() + "+");
                speakText = "加";
                break;
            case "-":
                field.setText(field.getText() + "-");
                speakText = "减";
                break;
            case ".":
                if (oldText.isEmpty()) {
                    //输入框为空时自动追加0
                    field.setText("0.");
                } else {
                    field.setText(oldText + ".");
                }
                speakText = "点";
                break;
            default:
                field.setText(oldText + text);
                // 新增：语音提示
                // 数字直接使用文本
                speakText = text;
                break;
        }

        // 只有非空语音文本且声音开启时才调用语音合成
        if (!speakText.isEmpty() && isSoundOn) {
            speak(speakText);
        }
    }

    private void calculate() {
        String expression = field.getText();
        // 实行计算操作，使用 js 计算引擎
        ScriptEngineManager manager = new ScriptEngineManager();
        // 初始化计算引擎 使用 javascript 解析数学表达式
        ScriptEngine engine = manager.getEngineByName("javascript");
        try {
            //计算表达式
            Object result = engine.eval(expression);
            field.setText(result.toString());
            history.add(expression + " = " + result);
        } catch (ScriptException e) {
            field.setText("运算错误");
            // 新增：语音提示
            if (isSoundOn) {
                speak("运算错误");
            }
            e.printStackTrace();
        }
    }

    //speak()方法根在 Windows 系统上使用 PowerShell 调用Add-Type -AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak("文本")
    private void speak(String text) {
        // 先进行基础转义
        String escapedText = text
                .replace("\"", "\\\"")
                .replace("'", "\\'")
                .replace("&", "\\&")
                .replace("|", "\\|")
                .replace(">", "\\>")
                .replace("<", "\\<")
                .replace("(", "\\(")
                .replace(")", "\\)");

        try {
            String osName = System.getProperty("os.name").toLowerCase();
            ProcessBuilder pb;

            if (osName.contains("win")) {
                // Windows 系统：使用更安全的 PowerShell 命令构造
                String script = String.format(
                        "$speakText = '%s'; " +
                                "Add-Type -AssemblyName System.Speech; " +
                                "(New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak($speakText)",
                        escapedText.replace("'", "''") // PowerShell 单引号转义
                );

                pb = new ProcessBuilder("powershell", "-Command", script);
            } else if (osName.contains("mac")) {
                pb = new ProcessBuilder("say", escapedText);
            } else if (osName.contains("nix") || osName.contains("nux")) {
                pb = new ProcessBuilder("espeak", "-v", "en-us", escapedText);
            } else {
                System.out.println("不支持当前操作系统的语音合成");
                return;
            }

            // 重定向错误流以获取详细日志
            pb.redirectErrorStream(true);
            Process process = pb.start();

            // 读取命令输出（用于调试）
            java.io.InputStream is = process.getInputStream();
            java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
            String output = s.hasNext() ? s.next() : "";

            int exitCode = process.waitFor();
            System.out.println("语音命令执行结果 - 文本: " + text + ", 退出码: " + exitCode);
            if (output.trim().length() > 0) {
                System.out.println("命令输出: " + output);
            }

            if (exitCode != 0) {
                System.err.println("语音合成命令执行失败，退出码: " + exitCode);
            }
        } catch (IOException | InterruptedException e) {
            System.err.println("语音合成异常: " + e.getMessage());
            e.printStackTrace();
        }
    }

    // 实现接口中的抽象方法 处理事件
    @Override
    public void actionPerformed(ActionEvent a) {
        // 此处可以捕获到 ActionListener 监听的事件
        Object source = a.getSource();
        if (source instanceof JButton) {
            JButton btn = (JButton) source;
            String text = btn.getText();
            setBtnText(text);
        }
    }

    public static void main(String[] args) {
        // 按顺序执行。这避免了直接操作 GUI 导致的线程竞争问题。
        // invokeLater 会将传入的 Runnable 任务（此处为 Calculator::new 方法引用）放入 EDT 的任务队列，由 EDT
        SwingUtilities.invokeLater(Calculator::new);
    }
}